package com.snap.vseries.utils;

import android.util.Log;

import org.apache.poi.util.IOUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.SyncFailedException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.runtime.RuntimeSchema;

public class FileUtils {

    private static final String TAG = "FileUtils";

    /**
     * 创建文件
     */
    public static void mkFile(File file) {
        if (!file.exists()) {
            file.getParentFile().mkdirs();
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Log.e(TAG, "保存的路径: " + file.getAbsolutePath());
    }

    /**
     * 创建目录
     */
    public static void mkDirectory(File dir) {
        if (!dir.isDirectory()) {
            dir.delete();
        }
        if (!dir.exists()) {
            dir.mkdirs();
        }
        android.util.Log.e(TAG, "保存的路径: " + dir.getAbsolutePath());
    }

    /**
     * 覆盖文件
     */
    public static void mkFileAndCover(File file) {
        if (!file.exists()) {
            file.getParentFile().mkdirs();
        } else {
            file.delete();
        }
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.e(TAG, "保存的路径: " + file.getAbsolutePath());
    }

    /**
     * 写文件，先Gzip压缩
     */
    public static boolean writeFile(File file, byte[] data) {
        GZIPOutputStream gos = null;
        try {
            gos = new GZIPOutputStream(new FileOutputStream(file));
            gos.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            if (gos != null) {
                try {
                    gos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        ensureWriteFile(file);
        return true;
    }

    /**
     * //防止断电导致的文件记录不成功
     * //对于输出设备或磁盘文件，flush只能保证数据到达内核缓冲区，
     * //并不能保证数据到达物理设备, 因此应该在调用flush后，调用sync()，确保数据存入磁盘。
     * //fileOutputStream.getFD().sync()在此无效果？不太明拔
     */
    public static void ensureWriteFile(File file) {
        FileInputStream fos = null;
        try {
            fos = new FileInputStream(file);
            fos.getFD().sync();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (SyncFailedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 读文件，先Gzip解压
     */
    public static byte[] readFile(File file) {
        FileInputStream fis = null;
        GZIPInputStream gis = null;
        ByteArrayOutputStream bos = null;
        try {
            fis = new FileInputStream(file);
            gis = new GZIPInputStream(fis);
            bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int n;
            while ((n = gis.read(buffer)) != -1) {
                bos.write(buffer, 0, n);
            }
            return bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (gis != null) {
                try {
                    gis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        Class<T> clazz = (Class<T>) obj.getClass();
        RuntimeSchema<T> schema = RuntimeSchema.createFrom(clazz);
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
    }

    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        RuntimeSchema<T> schema = RuntimeSchema.createFrom(clazz);
        T message = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(data, message, schema);
        return message;
    }

    public static void copyInputStreamToFile(final InputStream source, final File file) throws IOException {
        try {
            if (file.exists()) {
                if (file.isDirectory()) {
                    throw new IOException("File '" + file + "' exists but is a directory");
                }
                if (!file.canWrite()) {
                    throw new IOException("File '" + file + "' cannot be written to");
                }
            } else {
                final File parent = file.getParentFile();
                if (parent != null) {
                    if (!parent.mkdirs() && !parent.isDirectory()) {
                        throw new IOException("Directory '" + parent + "' could not be created");
                    }
                }
            }
            final FileOutputStream output = new FileOutputStream(file, false);
            try {
                IOUtils.copy(source, output);
                output.close(); // don't swallow close Exception if copy completes normally
            } finally {
                IOUtils.closeQuietly(output);
            }
        } finally {
            IOUtils.closeQuietly(source);
        }
    }

    /**
     * 检测文件名是否合法:要求只能输入数字，字母，下划线，横杆
     *
     * @param name
     * @return
     */
    public static boolean checkFileName(String name) {
        Matcher matcher = Pattern.compile("^[0-9a-zA-Z _-]+$").matcher(name);
        return matcher.find();
    }

    public static byte[] readNormalFile(File file) {
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        try {
            fis = new FileInputStream(file);
            bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int n;
            while ((n = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, n);
            }
            return bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static String readJsonFile(File file) {
        FileReader reader = null;
        try {
            reader = new FileReader(file);
            char[] buf = new char[1024];
            int num = 0;
            StringBuffer sb = new StringBuffer();
            while ((num = reader.read(buf)) != -1) {
                sb.append(new String(buf, 0, num));
            }
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (reader != null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
